#!/usr/bin/env python

import pygame
from pygame.locals import *

import sys
import pgu

from pgu import gui

import debug
import text
import tile
import unit

from debug import *
from helper import get_prop

TO_N = 0
TO_L = -1
TO_R = 1
TO_D = -3
TO_U = 3
TO_UL = TO_L + TO_U
TO_UR = TO_R + TO_U
TO_DL = TO_L + TO_D
TO_DR = TO_R + TO_D

scroll_dir = { TO_N: 'no_direction',
               TO_L: 'left',
               TO_R: 'right',
               TO_U: 'up',
               TO_D: 'down',
               TO_UL: 'upper_left',
               TO_UR: 'upper_right',
               TO_DL: 'lower_left',
               TO_DR: 'lower_right'
              }

class Icon(gui.Widget):
  def __init__(self, surface, **properties):
    gui.Widget.__init__(self, **properties)
    self.surface = surface
    self.update_rect = pygame.Rect(0, 0, self.surface.get_width(), self.surface.get_height())
  
  def set(self, surface):
    self.surface = surface
  
  def paint(self, surface):
    self.update_rect = surface.blit(self.surface, (0, 0))
  
  def update(self, surface):
    return [self.update_rect]
  
  def resize(self, width=None, height=None):
    return self.surface.get_width(), self.surface.get_height()

class Label(gui.Widget):
  def __init__(self, t, **properties):
    gui.Widget.__init__(self, **properties)
    self.text = t
    self.font = get_prop(properties, 'font', text.get_system_font())
    self.color = get_prop(properties, 'color', (255, 255, 255))
    self.antialias = get_prop(properties, 'antialias', False)
    
    self.create_surface()
    self.update_rect = pygame.Rect(0, 0, self.surface.get_width(), self.surface.get_height())
    
  def create_surface(self):
    self.surface = self.font.render(self.text, self.antialias, self.color)
  
  def set_text(self, t):
    self.text = t
    self.create_surface()
  
  def paint(self, surface):
    self.update_rect = surface.blit(self.surface, (0, 0))
  
  def update(self, surface):
    return [self.update_rect]
  
  def resize(self, width=None, height=None):
    return self.surface.get_width(), self.surface.get_height()


class OverlayContainer(gui.Container):
  def __init__(self, screen_dimensions, **properties):
    gui.Container.__init__(self, **properties)
    self.added = []
    self.mouse_xy = (-1, -1)
    self.screen_dimen = screen_dimensions
    
    #print gui.Container.resize(self)
  
  def add(self, w, x, y):
    self.added.append((w, x, y))
    gui.Container.add(self, w, x, y)
  
  def remove(self, w):
    def find_widget(w):
      for i in range(0, len(self.added)):
        w_tup = self.added[i]
        if w_tup[0] == w:
          return i
      return -1
    
    i = find_widget(w)
    if i == -1: return
    self.added.pop(i)
    gui.Container.remove(self, w)
  
  def event(self, event):
    if event.type in [pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP]:
      self.mouse_xy = event.pos
    
    if self.mouse_xy != (-1, -1):
      scr_w, scr_h = self.screen_dimen
      mouse_x, mouse_y = self.mouse_xy
      for widget, x, y in self.added:
        if widget.request_fixed_position(): continue
        old_x, old_y = x, y
        w_w, w_h = widget.resize()
        if mouse_x < scr_w / 2:
          if x < w_w / 2: x = scr_w - x - w_w
        else:
          if x > w_w / 2: x = scr_w - x - w_w
        if old_x != x or old_y != y:
          self.remove(widget)
          self.add(widget, x, y)
    
    #TODO if there is at least one overlay requesting modality,
    #disable all non-modal-requesting overlays before calling the
    #function below
    gui.Container.event(self, event)
  

NONE = 'none_surface'
ICON_WIDTH = 18
ICON_HEIGHT = 18

class InfoOverlay(gui.Table):
  def __init__(self, tile_engine, unit_engine, **properties):
    gui.Table.__init__(self, **properties)
    
    self.font = get_prop(properties, 'font', text.get_system_font())
    self.bg = get_prop(properties, 'bg', (0, 0, 0, 100))
    self.fg = get_prop(properties, 'fg', (255, 255, 255))
    self.antialias = get_prop(properties, 'antialias', False)
    
    self.defense_indicator = get_prop(properties, 'defense_indicator', 
                                  self.font.render('D:', self.antialias, self.fg))
    self.hp_indicator = get_prop(properties, 'hp_indicator', 
                                  self.font.render('HP:', self.antialias, self.fg))
    self.fuel_indicator = get_prop(properties, 'fuel_indicator', 
                                  self.font.render('Fuel:', self.antialias, self.fg))
    
    self.tile_engine = tile_engine
    self.unit_engine = unit_engine
    
    self.accessed_tile_surfaces = { }
    self.accessed_tile_names = { }
    self.accessed_unit_surfaces = { }
    self.accessed_unit_names = { }
    
    t_surf = pygame.Surface((ICON_WIDTH, ICON_HEIGHT), SRCALPHA, 32).convert_alpha()
    t_surf.fill((0, 0, 0, 0))
    
    self.accessed_tile_surfaces[NONE] = t_surf
    self.accessed_unit_surfaces[NONE] = t_surf
    self.accessed_unit_names[NONE] = self.render_text(' ')
    
    self.w, self.h = 140, 70
    
    self.create_bg_surface()
    self.create_transparent_surface()
    
    self.form = gui.Form()
    
    #self.tile_stat_table = gui.Table()
    
    self.tr()
    self.td(Icon(self.render_text(''), name='tile_name'), colspan=2, align=-1)
    self.td(Icon(self.render_text(''), name='unit_name'), colspan=2, align=-1)
    
    self.tr()
    self.td(Icon(t_surf, name='tile_surface'), colspan=2, align=-1)
    self.td(Icon(t_surf, name='unit_surface'), align=0)
    
    self.tr()
    self.td(gui.Spacer(2, 2), colspan=4)
    
    self.tr()
    self.td(Icon(t_surf, name='d_ind'), align=-1)
    self.td(Icon(t_surf, name='d_val'), align=-1)
    self.td(Icon(t_surf, name='h_ind'), align=0)
    self.td(Icon(t_surf, name='h_val'), align=-1)
    
    self.tr()
    self.td(Icon(t_surf))
    self.td(Icon(t_surf))
    self.td(Icon(t_surf, name='f_ind'), align=0)
    self.td(Icon(t_surf, name='f_val'), align=-1)
  
  def render_text(self, t, with_whitespace=False):
    length = 10
    if len(t) < length:
      whitespace = ' ' * (length - len(t))
    else:
      whitespace = ''
    if with_whitespace:
      return self.font.render('%s%s' % (t, whitespace), self.antialias, self.fg)
    else:
      return self.font.render(t, self.antialias, self.fg)
  
  def create_icon(self, surf):
    s_w, s_h = surf.get_width(), surf.get_height()
    ratio = float(ICON_HEIGHT) / float(s_h)
    return pygame.transform.rotozoom(surf, 0, ratio)
  
  def create_bg_surface(self):
    self.bg_surface = pygame.Surface((self.w, self.h), SRCALPHA, 32).convert_alpha()
    self.bg_surface.fill(self.bg)
  
  def create_transparent_surface(self):
    self.transp_surface = pygame.Surface((self.w, self.h), SRCALPHA, 32).convert_alpha()
    self.transp_surface.fill((0, 0, 0, 0))
  
  def request_modal(self):
    return False
  
  def request_fixed_position(self):
    return False
  
  
  def handle_cursor_select(self, new_grid_xy):
    pass
  
  def handle_cursor_cancel(self, new_grid_xy):
    pass
  
  def handle_cursor_change(self, new_grid_xy):
    
    tile_type = self.tile_engine.get_tile_type_on_grid(new_grid_xy)
    t_name = tile_type.get_name()
    t_short_name = tile_type.get_short_name()
    def_value = tile_type.get_defense_value()
    
    if t_short_name not in self.accessed_tile_names:
      self.accessed_tile_names[t_short_name] = self.render_text(t_short_name)
      self.accessed_tile_surfaces[t_short_name] = self.create_icon(tile_type.get_surface())
    
    #print t_short_name, self.accessed_tile_surfaces[t_short_name].get_height()
    
    self.form['tile_name'].set(self.accessed_tile_names[t_short_name])
    self.form['tile_surface'].set(self.accessed_tile_surfaces[t_short_name])
    
    self.form['d_ind'].set(self.defense_indicator)
    self.form['d_val'].set(self.render_text(str(def_value)))
    #print def_value
    
    u = self.unit_engine.get_unit_on_grid(new_grid_xy)
    
    if u is not None:
      unit_type = u.get_type()
      u_name = unit_type.get_name()
      u_short_name = unit_type.get_short_name()
      
      hp = u.get_property('hp')
      max_hp = unit_type.get_prop('max_hp')
      
      fuel = u.get_property('fuel')
      max_fuel = unit_type.get_prop('max_fuel')
      
      if u_name not in self.accessed_unit_names:
        self.accessed_unit_names[u_name] = self.render_text(u_short_name)
        self.accessed_unit_surfaces[u_name] = self.create_icon(unit_type.get_anim(unit.STATE_NORMAL).update())
      self.form['unit_name'].set(self.accessed_unit_names[u_name])
      self.form['unit_surface'].set(self.accessed_unit_surfaces[u_name])
      self.form['h_ind'].set(self.hp_indicator)
      self.form['h_val'].set(self.render_text('%d/%d' % (hp, max_hp), with_whitespace=False))
      self.form['f_ind'].set(self.fuel_indicator)
      self.form['f_val'].set(self.render_text('%d/%d' % (fuel, max_fuel), with_whitespace=False))
    else:
      #u_name = NONE
      self.form['unit_name'].set(self.accessed_unit_names[NONE])
      self.form['unit_surface'].set(self.accessed_unit_names[NONE])
      self.form['h_ind'].set(self.accessed_unit_names[NONE])
      self.form['h_val'].set(self.accessed_unit_names[NONE])
      self.form['f_ind'].set(self.accessed_unit_names[NONE])
      self.form['f_val'].set(self.accessed_unit_names[NONE])
    
  def paint(self, surface):
    if not self.unit_engine.is_unit_selected():
      surface.blit(self.bg_surface, (0, 0))
      gui.Table.paint(self, surface)
    else:
      #surface.blit(self.transp_surface, (0, 0))
      pass
  
  def resize(self, width=None, height=None):
    
    t_w, t_h = gui.Table.resize(self, width, height)
    if self.w > t_w or self.h > t_h:
      return self.w, self.h
    else:
      return t_w, t_h

class AttackOverlay(gui.Table):
  def __init__(self, unit_engine, **properties):
    self.unit_engine = unit_engine
    #TODO flesh out logic

class MapViewport(gui.Widget):
  def __init__(self, scr_dimensions, engine, **properties):
    gui.Widget.__init__(self, **properties)
    
    #use this if you don't want the viewport to do anything ;)
    #self.disabled = True 
    
    w, h = scr_dimensions
    x, y = 0, 0
    
    #self.blank_tile = blank_tile
    #self.cursor = cursor
    self.engine = engine
    
    map_rect = self.engine.update().get_rect()
    if map_rect.w < w: w = map_rect.w
    if map_rect.h < h: h = map_rect.h

    self.output_surface = pygame.Surface((w, h), HWSURFACE|SRCALPHA, 32).convert_alpha()
    self.output_surface.fill((0, 0, 0))
    self.set_viewport(0, 0, w, h)
    
    self.cursor_listeners = []
    
    self.dx = 0
    self.dy = 0
    self.dvx = 2
    self.dvy = 2
    self.max_dx = 5
    self.max_dy = 5
    
    self.scroll_margin = get_prop(properties, 'scroll_margin', 20)
    
    self.cursor_xy = (0, 0)
    self.cursor_grid_xy = (0, 0)
    self.is_moving = {'l': False, 
                      'r': False, 
                      'u': False, 
                      'd': False}
    
    self.mouse_xy = (-1, -1)
    self.mouse_button = None
  
  def register_cursor_listener(self, c_listener):
    self.cursor_listeners.append(c_listener)
  
  def set_pan_directions(self, pan_dir):
    l, r, u, d = pan_dir
    self.is_moving['l'] = l
    self.is_moving['r'] = r
    self.is_moving['u'] = u
    self.is_moving['d'] = d
  
  def set_cursor_position(self, mouse_xy):
    
    tile_w, tile_h = self.engine.get_tile_wh()
    grid_x, grid_y = self.viewport_to_grid(mouse_xy)
    off_x, off_y = self.get_offset()
    
    self.cursor_xy = (grid_x * tile_w - off_x, grid_y * tile_h - off_y)
    
    self.update_grid_xy(mouse_xy)
    
    for c_listener in self.cursor_listeners:
      c_listener.handle_cursor_change(self.cursor_grid_xy)
    #self.cursor_grid_xy = (grid_x, grid_y)
  
  def set_cursor_select(self, mouse_xy):
    self.set_cursor_position(mouse_xy)
    
    for c_listener in self.cursor_listeners:
      c_listener.handle_cursor_select(self.cursor_grid_xy)
  
  def get_cursor_grid_xy(self):
    return self.cursor_grid_xy
  
  def get_engine(self):
    return self.engine
  
  #gets the offset of the viewport's xy with the start of a tile
  def get_offset(self):
    viewp_x, viewp_y = self.viewport.x, self.viewport.y
    tile_w, tile_h = self.engine.get_tile_wh()
    return (viewp_x % tile_w, viewp_y % tile_h)
  
  def update_grid_xy(self, mouse_xy):
    viewp_x, viewp_y = self.viewport.x, self.viewport.y
    mouse_x, mouse_y = mouse_xy
    real_x, real_y = viewp_x + mouse_x, viewp_y + mouse_y
    self.cursor_grid_xy = self.engine.real_to_grid((real_x, real_y))
  
  def viewport_to_grid(self, v_xy):
    v_x, v_y = v_xy
    off_x, off_y = self.get_offset()
    g_x, g_y = self.engine.real_to_grid((v_x + off_x, v_y + off_y))
    return (g_x, g_y)
  
  def set_viewport(self, x, y, w, h):
    self.viewport = pygame.Rect(x, y, w, h)
  
  def draw_tile(self, src, grid_xy):
    return draw_tile(src, self.output_surface, 
                    self.engine.get_tile_wh(), 
                    grid_xy, 
                    start_offset=self.get_offset())
  
  def update_xy(self, limits):
    begin_time = pygame.time.get_ticks()
    
    x, y, w, h = self.viewport.x, self.viewport.y, self.viewport.w, self.viewport.h
    max_x, max_y = limits
    
    if x + self.dx <= 0: x = 0
    elif x + self.dx >=max_x: x = max_x
    else: x += self.dx
    
    if y + self.dy <= 0: y = 0
    elif y + self.dy >= max_y: y = max_y
    else: y += self.dy
    
    if self.is_moving['l']:
      if self.dx > -self.max_dx:
        self.dx -= self.dvx
    elif self.is_moving['r']:
      if self.dx < self.max_dx:
        self.dx += self.dvx
    else:
      if self.dx > 0: self.dx -= 1
      elif self.dx < 0: self.dx += 1
    
    if self.is_moving['u']:
      if self.dy > -self.max_dy:
        self.dy -= self.dvy
    elif self.is_moving['d']:
      if self.dy < self.max_dy:
        self.dy += self.dvy
    else:
      if self.dy > 0: self.dy -= 1
      elif self.dy < 0: self.dy += 1
    
    end_time = pygame.time.get_ticks()
    debug('update_xy: took %d msecs.' % (end_time - begin_time))

    self.set_viewport(x, y, w, h)
  
  def request_fixed_position(self):
    return True
  
  def request_modal(self):
    return False
  
  def event(self, event):
    if event.type in [pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP]:
      self.mouse_xy = event.pos
    if event.type == pygame.MOUSEBUTTONUP:
      self.mouse_button = event.button
      info('Mouse button %s clicked.' % (str(self.mouse_button)))

  def paint(self, surface):
    mouse_x, mouse_y = self.mouse_xy
    if self.mouse_xy != (-1, -1):
      s_width, s_height = self.viewport.w, self.viewport.h
      
      dir = mouse_x < self.scroll_margin and TO_L or 0
      dir = mouse_x > s_width - self.scroll_margin and dir + TO_R or dir
      dir = mouse_y < self.scroll_margin and dir + TO_U or dir
      dir = mouse_y > s_height - self.scroll_margin and dir + TO_D or dir
      
      ll = dir in [TO_L, TO_DL, TO_UL]
      rr = dir in [TO_R, TO_DR, TO_UR]
      uu = dir in [TO_U, TO_UL, TO_UR]
      dd = dir in [TO_D, TO_DL, TO_DR]
      self.set_pan_directions((ll, rr, uu, dd))
      
      self.set_cursor_position(self.mouse_xy)
    
    #left mouse button
    if self.mouse_button == 1:
      self.set_cursor_select(self.mouse_xy)
      self.mouse_button = None
    
    
    map = self.engine.update()
    map_rect = map.get_rect()
    
    if map_rect.w > self.viewport.w or map_rect.h > self.viewport.h:
      self.update_xy((map_rect.w - self.viewport.w, map_rect.h - self.viewport.h))
    
    #self.output_surface.blit(map, (0, 0), self.viewport)
    self.output_surface = map.subsurface(self.viewport)
    
    #return self.output_surface
    surface.blit(self.output_surface, (0, 0))
  
  def resize(self, width=None, height=None):
    return self.viewport.w, self.viewport.h


if __name__ == "__main__":
  pass
  
  
